package com.bumptech.glide.load.data;

import com.bumptech.glide.load.engine.bitmap_recycle.ArrayPool;
import com.bumptech.glide.load.resource.bitmap.RecyclableBufferedInputStream;
import com.bumptech.glide.util.LogUtil;
import com.bumptech.glide.util.Synthetic;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * Implementation for {@link InputStream}s that rewinds streams by wrapping them in a buffered
 * stream.
 */
public final class InputStreamRewinder implements DataRewinder<InputStream> {
    // 5MB.
    private static final int MARK_READ_LIMIT = 5 * 1024 * 1024;

    private final RecyclableBufferedInputStream bufferedStream;
    private final InputStreamCache mCacheInputStream;

    @Synthetic
    public InputStreamRewinder(InputStream is, ArrayPool byteArrayPool) {
        // We don't check is.markSupported() here because RecyclableBufferedInputStream allows resetting
        // after exceeding MARK_READ_LIMIT, which other InputStreams don't guarantee.

        mCacheInputStream = new InputStreamCache(is);
        bufferedStream = new RecyclableBufferedInputStream(mCacheInputStream.getInputStream(), byteArrayPool);  //previously own process
        bufferedStream.mark(MARK_READ_LIMIT);
    }

    /**
     * getCacheInputStream
     * <p>
     * To solve the problem that the InputStream can't be read twice,
     * in some cases,we need to read the InputStream once to obtain the width and
     * height of the InputStream{@link InputStreamCache}, Because the InJustDecodeBounds attribute is unavailable,
     * only the width and height of the inputStream are obtained,
     * but the image data is not red
     * </>
     *
     * @return InputStream
     */
    public InputStream getCacheInputStream() {
        return mCacheInputStream.getInputStream();
    }

    @NotNull
    @Override
    public InputStream rewindAndGet() throws IOException {
        bufferedStream.reset();
        return bufferedStream;
    }

    @Override
    public void cleanup() {
        bufferedStream.release();
    }

    public void fixMarkLimits() {
        bufferedStream.fixMarkLimit();
    }

    /**
     * Factory for producing {@link com.bumptech.glide.load.data.InputStreamRewinder}s from {@link
     * InputStream}s.
     */
    public static final class Factory implements DataRewinder.Factory<InputStream> {
        private final ArrayPool byteArrayPool;

        public Factory(ArrayPool byteArrayPool) {
            this.byteArrayPool = byteArrayPool;
        }

        @NotNull
        @Override
        public DataRewinder<InputStream> build(InputStream data) {
            return new InputStreamRewinder(data, byteArrayPool);
        }

        @NotNull
        @Override
        public Class<InputStream> getDataClass() {
            return InputStream.class;
        }
    }


    /**
     * 缓存InputStream，以便InputStream复用
     */
    private class InputStreamCache {
        private ByteArrayOutputStream byteArrayOutputStream;

        public InputStreamCache(InputStream inputStream) {
            if (inputStream == null) {
                return;
            }
            byteArrayOutputStream = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len;
            try {
                while ((len = inputStream.read(buffer)) > -1) {
                    byteArrayOutputStream.write(buffer, 0, len);
                }
                byteArrayOutputStream.flush();
                byteArrayOutputStream.close();
            } catch (IOException e) {
                LogUtil.error("InputStreamCache", "error message :" + e.getMessage());

            }
        }

        /**
         * 获取缓存大小
         *
         * @return int
         */
        public int getCacheSize() {
            if (byteArrayOutputStream == null) {
                return MARK_READ_LIMIT;
            }
            return byteArrayOutputStream.size();
        }

        /**
         * 获取缓存流
         *
         * @return InputStream
         */
        public InputStream getInputStream() {
            if (byteArrayOutputStream == null) {
                return null;
            }
            return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        }
    }
}
